package logic

import (
	"context"
	"encoding/json"
	"go.uber.org/zap"
	"strconv"
	"wms/dao/mysql"
	"wms/dao/redis"
	"wms/define"
	"wms/logger"
	"wms/models"
	"wms/pkg/snowflake"
)

// RoleMenuToMenuReply 获取菜单列表返回
func RoleMenuToMenuReply(roleMenus []*define.RoleMenu) []*define.MenuReply {
	reply := make([]*define.MenuReply, 0)
	// 一层循环，得到顶层菜单
	for _, v := range roleMenus {
		if v.ParentId == 0 {
			reply = append(reply, &define.MenuReply{
				Identity: v.Identity,
				Name:     v.Name,
				WebIcon:  v.WebIcon,
				Sort:     v.Sort,
				Path:     v.Path,
				Level:    v.Level,
				SubMenus: GetChildrenMenu(v.Id, v.Identity, roleMenus),
			})
		}
	}
	return reply
}

// GetChildrenMenu 获取子菜单
func GetChildrenMenu(parentId int, parentIdentity string, roleMenus []*define.RoleMenu) []*define.MenuReply {
	data := make([]*define.MenuReply, 0)
	for _, v := range roleMenus {

		if v.ParentId == parentId {
			menu := &define.MenuReply{
				Identity:       v.Identity,
				Name:           v.Name,
				WebIcon:        v.WebIcon,
				Sort:           v.Sort,
				Path:           v.Path,
				Level:          v.Level,
				ParentIdentity: parentIdentity,
				SubMenus: []*define.MenuReply{
					{Crud: 1, Name: "查找", Identity: v.Identity + "1"},
					{Crud: 2, Name: "增加", Identity: v.Identity + "2"},
					{Crud: 4, Name: "修改", Identity: v.Identity + "4"},
					{Crud: 8, Name: "删除", Identity: v.Identity + "8"},
				},
			}
			data = append(data, menu)
		}
	}
	return data
}

// GetMenuList 获取菜单列表
func GetMenuList(mc *define.MyClaims, data *[]*define.MenuReply) error {
	menuField := mc.RoleIdentity
	if mc.IsAdmin {
		menuField = "ADMIN"
	}

	// 1. 从缓存获取
	res, err := redis.RDB.HGet(context.Background(), define.RedisMenuPrefix, menuField).Result()
	if err != nil {
		// 缓存不存在，或者获取失败，从 mysql 中获取，并记入缓存
		zap.L().Error("redis not exist menu", zap.Error(err))

		tx, err1 := mysql.GetRoleMenusByRoleIdentity(mc.RoleIdentity, mc.IsAdmin)
		if err1 != nil {
			zap.L().Error("mysql.GetRoleMenusByRoleIdentity failed", zap.Error(err1))
			return err1
		}

		roleMenus := make([]*define.RoleMenu, 0)
		if err := tx.Scan(&roleMenus).Error; err != nil {
			zap.L().Error("mysql.GetRoleMenusByRoleIdentity Scan failed", zap.Error(err))
			return err
		}

		// RoleMenu 结构转为 MenuReply 返回
		*data = RoleMenuToMenuReply(roleMenus)
		// 输出一下获取的菜单列表信息
		logger.LogGetMenusName(*data)

		// JSON 序列化，存入 redis
		b, _ := json.Marshal(*data)
		redis.RDB.HSet(context.Background(), define.RedisMenuPrefix, map[string]string{
			menuField: string(b),
		})
	} else {
		// 缓存存在，直接返回，并延时缓存
		err = json.Unmarshal([]byte(res), data)

		if err != nil {
			zap.L().Error("json.Unmarshal failed", zap.String("menuField", menuField), zap.Error(err))
			return err
		}
		redis.RDB.Expire(context.Background(), define.RedisMenuPrefix, define.RedisExpireDurationWeek)
	}
	return nil
}

// AddOneMenu 增加一个菜单项
func AddOneMenu(p *define.ParamMenuAdd) error {
	// 1. 有父级菜单，先获取父级 ID
	var parentId uint
	if p.ParentIdentity != "" {
		tx := mysql.GetMenuIdByIdentity(p.ParentIdentity)
		if err := tx.Find(&parentId).Error; err != nil {
			zap.L().Error("mysql.GetMenuIdByIdentity failed", zap.Error(err))
			return err
		}
	}

	// 2. mysql 入库菜单数据
	mb := &models.MenuBasic{
		Identity: strconv.FormatUint(snowflake.GetUint64ID(), 10),
		ParentId: parentId,
		Name:     p.Name,
		WebIcon:  p.WebIcon,
		Path:     p.Path,
		Sort:     p.Sort,
		Level:    p.Level,
	}

	// 3. 执行插入
	if err := mysql.InsertOneMenu(mb); err != nil {
		zap.L().Error("mysql.InsertOneMenu failed", zap.Error(err))
		return err
	}

	// 4. redis 删除缓存
	err := redis.RDB.Del(context.Background(), define.RedisMenuPrefix).Err()
	if err != nil {
		zap.L().Error("redis del cache error", zap.Error(err))
		return err
	}

	// 5. 如果不是一级菜单，那么插入菜单对应的 4 个权限到 function_basic
	fbs := make([]*models.FunctionBasic, 0)
	if p.Level != 0 {
		for i := 0; i < 4; i++ {
			var suf string
			switch i {
			case 0:
				suf = "/get"
			case 1:
				suf = "/create"
			case 2:
				suf = "/update"
			case 3:
				suf = "/delete"
			}

			fb := &models.FunctionBasic{
				Identity: strconv.FormatUint(snowflake.GetUint64ID(), 10),
				MenuId:   mb.ID,
				Name:     mb.Path + suf,
				Uri:      mb.Path + suf,
				Crud:     1 << i,
			}

			fbs = append(fbs, fb)
		}
	}

	return mysql.InsertFuncBasic(fbs)
}

// DelOneMenu 删除菜单，包含其子菜单
func DelOneMenu(identity string) error {
	// 1. 获取菜单的 parent_id
	tx := mysql.GetMenuParentIdByIdentity(identity)
	var menuParentId uint
	if err := tx.Find(&menuParentId).Error; err != nil {
		return err
	}

	// 所有需要删除的菜单的 identity
	identitys := make([]string, 0)
	// 2. 目录，需要获取子级菜单
	if menuParentId == 0 {
		// 3. 获取本级目录的原生 id
		var menuId uint
		tx := mysql.GetMenuIdByIdentity(identity)
		if err := tx.Find(&menuId).Error; err != nil {
			zap.L().Error("mysql.GetMenuIdByIdentity failed", zap.Error(err))
			return err
		}
		// 4. 获取子级菜单的 identitys
		tx1 := mysql.GetMenuIdentitysByParentId(menuId)
		if err := tx1.Scan(&identitys).Error; err != nil {
			zap.L().Error("mysql.GetMenuIdentitysByParentId failed", zap.Error(err))
			return err
		}
	}
	identitys = append(identitys, identity)

	// 4. 事务删除，menu_basic, role_menu, function_basic, role_function
	if err := mysql.TransactionDeleteMenu(identitys); err != nil {
		zap.L().Error("mysql.TransactionDeleteMenu failed", zap.Error(err))
		return err
	}

	// 5. redis 删除缓存
	// 这里不必删除角色中对应的权限uri缓存，暂时不影响实际操作
	err := redis.RDB.Del(context.Background(), define.RedisMenuPrefix).Err()
	if err != nil {
		zap.L().Error("redis del cache error", zap.Error(err))
		return err
	}

	return nil
}

// UpdateOneMenu 更新一个菜单项
func UpdateOneMenu(p *define.ParamMenuUpdate) error {
	// 1. 获取父级 Id
	var parentId uint
	tx := mysql.GetMenuIdByIdentity(p.ParentIdentity)
	if err := tx.Find(&parentId).Error; err != nil {
		zap.L().Error("mysql.GetMenuIdByIdentity failed", zap.Error(err))
		return err
	}

	// 2. 构造 menu_basic 表
	mb := &models.MenuBasic{
		Identity: p.Identity,
		ParentId: parentId,
		Name:     p.Name,
		WebIcon:  p.WebIcon,
		Path:     p.Path,
		Sort:     p.Sort,
		Level:    p.Level,
	}

	// 3. 执行更新
	if err := mysql.UpdateOneMenu(mb); err != nil {
		zap.L().Error("mysql.UpdateOneMenu failed", zap.Error(err))
		return err
	}

	// 4. redis 清空菜单缓存
	if err := redis.RDB.Del(context.Background(), define.RedisMenuPrefix).Err(); err != nil {
		zap.L().Error("redis del menu error", zap.Error(err))
		return err
	}

	return nil
}
